package it.dk.view;

import it.dk.model.Barrel;
import it.dk.model.Game;
import it.dk.model.Ladder;
import it.dk.model.Platform;

import org.newdawn.slick.Animation;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;

/**
 * Classe che si occupa di tenere memorizzate immagini ed animazioni
 * dei componenti statici e dinamici del gioco, inoltre contiene tutti
 * i metodi necessari per disegnarli a video.
 * 
 * Pattern utilizzato: Singleton
 */
public class BoardRender {
	private static final String PATH_IMAGE = "res/img/";
	private Game game;
	private Image ladder, platform, heroStopRight, heroStopLeft, rollingBarrel, princess;
	private Image background, winScreen, loseScreen, pauseScreen, homeScreen, demoScreen, rankingShowScreen, rankingSaveScreen;
	private Image heroJumpingRight, heroJumpingLeft, heroDead, heroRunningRight, heroRunningLeft, heroLifes, heroClimbing;
	private Animation rankingSaveScreenAnimation, rankingShowScreenAnimation, demoScreenAnimation, homeScreenAnimation, princessAnimation, heroRunningRightAnimation, heroRunningLeftAnimation, barrelAnimation;
	private boolean movingRight;
	
	private static final int ANIMATION_DURATION = 100;
	
	private static BoardRender instance = null;
	
	/**
	 * Costruttore privato vuoto della classe (richiesto dal pattern singleton)
	 */
	private BoardRender(){
		try{
			background = new Image(PATH_IMAGE + "background.png");
			ladder = new Image(PATH_IMAGE + "ladder_piece.png");
			platform = new Image(PATH_IMAGE + "platform_piece.png");
			rollingBarrel = new Image(PATH_IMAGE + "barrel.png");
			heroStopRight = new Image(PATH_IMAGE + "hero_initRight.png");
			heroStopLeft = new Image(PATH_IMAGE + "hero_initLeft.png"); 
			heroLifes = new Image(PATH_IMAGE + "heroLife.png");
			heroRunningRight = new Image(PATH_IMAGE + "hero_running_right.png");
			heroRunningLeft = new Image(PATH_IMAGE + "hero_running_left.png");
			heroJumpingRight = new Image(PATH_IMAGE + "hero_jumpDx.png");
			heroJumpingLeft = new Image(PATH_IMAGE + "hero_jumpSx.png");
			heroClimbing = new Image(PATH_IMAGE + "hero_climbStop.png");
			princess = new Image(PATH_IMAGE + "gift.png");
			winScreen = new Image(PATH_IMAGE + "winner.png");
			loseScreen = new Image(PATH_IMAGE + "loser.png");
			pauseScreen = new Image(PATH_IMAGE + "pause.png");
			homeScreen = new Image(PATH_IMAGE + "home.png");
			demoScreen = new Image(PATH_IMAGE + "demo.png");
			rankingShowScreen = new Image(PATH_IMAGE + "showRanking.png");
			rankingSaveScreen = new Image(PATH_IMAGE + "saveRanking.png");
			heroDead = new Image(PATH_IMAGE + "hero_dead.png");
		}catch(SlickException e){
			System.err.println("ERRORE CARICAMENTO IMMAGINI DI GIOCO.");
			e.printStackTrace();
		}
	}
	
	/**
	 * Metodo utilizzato per restituire l'unica istanza della classe
	 * (richiesto per l'implementazione del pattern singleton)
	 * 
	 * @return	l'unica istanza della classe
	 */
	public static BoardRender getInstance(){
		if(instance==null){
			instance = new BoardRender();
		}
		return instance;
	}
	
	/**
	 * Metodo di inizializzazione dell'unica istanza di BoardRender,
	 * che inizializzando tutti gli elementi grafici da utilizzare
	 * 
	 * @param game	oggetto che contiene tutti i componenti della schermata di gioco 
	 * @throws		eccezione della lireria Slick2D
	 */
	public void init(Game game) throws SlickException{
		this.game = game;
		movingRight = true;
		
		adaptImage2Resolution();
		
		heroRunningRightAnimation = new Animation(new SpriteSheet(heroRunningRight,
				game.getHeroWidth(), game.getHeroHeight()) , ANIMATION_DURATION);
	
		heroRunningLeftAnimation = new Animation(new SpriteSheet(heroRunningLeft,
				game.getHeroWidth(), game.getHeroHeight()) , ANIMATION_DURATION);
		
		barrelAnimation =  new Animation(new SpriteSheet(rollingBarrel,
				game.getBarrelSize(), game.getBarrelSize()) , ANIMATION_DURATION);
		
		princessAnimation =  new Animation(new SpriteSheet(princess,
				game.getPrincessWidth(), game.getPrincessHeight()) , ANIMATION_DURATION*4);
				
		homeScreenAnimation = new Animation(new SpriteSheet(homeScreen,
				game.getGameWidth(), game.getGameHeight()) , ANIMATION_DURATION*4);
		
		demoScreenAnimation = new Animation(new SpriteSheet(demoScreen,
				game.getGameWidth(), game.getGameHeight()) , ANIMATION_DURATION*4);
		
		rankingShowScreenAnimation = new Animation(new SpriteSheet(rankingShowScreen,
				game.getGameWidth(), game.getGameHeight()) , ANIMATION_DURATION*4);
		
		rankingSaveScreenAnimation = new Animation(new SpriteSheet(rankingSaveScreen,
				game.getGameWidth(), game.getGameHeight()) , ANIMATION_DURATION*4);
	}
	
	/**
	 * Metodo che disegna tutti gli elementi statici del gioco
	 * (scale e piattaforme) e i barili
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderScene(Graphics g, int lifes, int level, int points){
		
		g.drawImage(background, 0, 0);
		
		g.drawString("LIFES: ", 600, 10);
		for(int i = 1; i <= lifes; i++)
			heroLifes.draw(635+i*heroLifes.getWidth(), 5);
		
		g.drawString("LEVEL: " + (level+1), 10, 10);
		
		g.drawString("POINTS: " + points, 10, 30);
		
		for(Platform o : game.getPlatforms()){
			g.fillRect(o.getShape().getX(),
					o.getShape().getY(),
					o.getShape().getWidth(),
					o.getShape().getHeight(),
					platform,
					platform.getWidth()
					,0);
		}
		
		for(Ladder o : game.getLadders()){
			g.fillRect(o.getShape().getX(),
					o.getShape().getY(),
					o.getShape().getWidth(),
					o.getShape().getHeight(),
					ladder,
					0,
					ladder.getHeight());
		}
		
		for(Barrel o: game.getBarrels()){
			barrelAnimation.draw(o.getShape().getX(), o.getShape().getY());
		}
		
		princessAnimation.draw(game.getPrincess().getShape().getX(), game.getPrincess().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre corre verso destra
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroRunningRight(Graphics g){
		movingRight = true;
		heroRunningRightAnimation.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre corre a sinistra
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroRunningLeft(Graphics g){
		movingRight = false;
		heroRunningLeftAnimation.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre sale o scende una scala
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroClimbing(Graphics g){
		heroClimbing.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre salta sul posto
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroJumping(Graphics g){
		if(movingRight)
			heroJumpingRight.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
		else
			heroJumpingLeft.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre salta verso destra
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroJumpingRight(Graphics g){
		movingRight = true;
		heroJumpingRight.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre salta verso sinistra
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroJumpingLeft(Graphics g){
		movingRight = false;
		heroJumpingLeft.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe mentre sta fermo orientato rispetto all'ultima direzione presa
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroStopped(Graphics g){
		if(movingRight)
			heroStopRight.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
		else
			heroStopLeft.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna l'eroe quando muore
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void renderHeroDead(Graphics g){
		heroDead.draw(game.getHero().getShape().getX(), game.getHero().getShape().getY());
	}
	
	/**
	 * Metodo che disegna lo screen di vittoria
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawWinScreen(Graphics g){
		winScreen.draw(0, 0);
	}
	
	/**
	 * Metodo che disegna lo screen di sconfitta
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawLoseScreen(Graphics g){
		loseScreen.draw(0, 0);
	}
	
	/**
	 * Metodo che disegna lo screen di pausa
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawPauseScreen(Graphics g){
		pauseScreen.draw(0, 0);
	}
	
	/**
	 * Metodo che disegna lo screen di benvenuto
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawHomeScreen(Graphics g){
		homeScreenAnimation.draw(0, 0);
	}
	
	/**
	 * Metodo che disegna lo screen da sovrapporre alla demo
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawDemoScreen(Graphics g){
		demoScreenAnimation.draw(0, 0);
	}
	
	/**
	 * Metodo che disegna lo screen da sovrapporre alla schermata di Ranking
	 * durante il salvataggio di un nuovo punteggio
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawRankingSaveScreen(Graphics g){
		rankingSaveScreenAnimation.draw();;
	}
	
	/**
	 * Metodo che disegna lo screen da sovrapporre alla schermata di Ranking
	 * mentre si sta semplicemente visualizzando la classifica
	 * 
	 * @param g	oggetto Graphics che consente il render del generico oggetto grafico a video
	 */
	public void drawRankingShowScreen(Graphics g){
		rankingShowScreenAnimation.draw();;
	}

	
	/**
	 * Metodo che effettua il ridimensionamento degli oggetti grafici
	 * in base alla risoluzione della schermata di gioco
	 */
	public void adaptImage2Resolution(){
		int tileSize = game.getTileSize();
		int heroWidth = game.getHeroWidth();
		int heroHeight = game.getHeroHeight();
		
		background = background.getScaledCopy(game.getGameWidth(), game.getGameHeight());
		winScreen = winScreen.getScaledCopy(game.getGameWidth(), game.getGameHeight());
		loseScreen = loseScreen.getScaledCopy(game.getGameWidth(), game.getGameHeight());
		pauseScreen = pauseScreen.getScaledCopy(game.getGameWidth(), game.getGameHeight());
		homeScreen = homeScreen.getScaledCopy(2*game.getGameWidth(), game.getGameHeight());
		demoScreen = demoScreen.getScaledCopy(2*game.getGameWidth(), game.getGameHeight());
		rankingShowScreen = rankingShowScreen.getScaledCopy(2*game.getGameWidth(), game.getGameHeight());
		rankingSaveScreen = rankingSaveScreen.getScaledCopy(2*game.getGameWidth(), game.getGameHeight());
		ladder = ladder.getScaledCopy(tileSize, tileSize);
		platform = platform.getScaledCopy(tileSize, tileSize);
		heroStopRight = heroStopRight.getScaledCopy(heroWidth, heroHeight);
		heroStopLeft = heroStopLeft.getScaledCopy(heroWidth, heroHeight);
		heroLifes = heroLifes.getScaledCopy(game.getLifesIconWidth(), game.getLifesIconHeight());
		heroRunningRight = heroRunningRight.getScaledCopy(5*heroWidth,heroHeight);
		heroRunningLeft = heroRunningLeft.getScaledCopy(5*heroWidth,heroHeight);
		heroJumpingRight = heroJumpingRight.getScaledCopy(heroWidth,heroHeight);
		heroJumpingLeft = heroJumpingLeft.getScaledCopy(heroWidth,heroHeight);
		heroDead = heroDead.getScaledCopy(heroWidth, heroHeight);
		heroClimbing = heroClimbing.getScaledCopy(heroWidth,heroHeight);
		rollingBarrel = rollingBarrel.getScaledCopy(5*game.getBarrelSize(), game.getBarrelSize());
		princess = princess.getScaledCopy(4*game.getPrincessWidth(), game.getPrincessHeight());
	}
}
